<html xmlns="http://www.w3.org/1999/xhtml"><head>
<script type="text/javascript" src="../jsxqueryparser/XQueryParser.js"></script>
<script type="text/javascript" src="../jsxqueryparser/XQueryFunctionsUnicodeNormalizationPlugin.js"></script>
<script type="text/javascript">

function whatIs(object) {
    var text = "";
    text += "type: " + (object instanceof Array ? "array" : object instanceof Date ? "Date" : object instanceof RegExp ? "RegExp" : typeof object) + "\n";
    if (object === null) {
        text += "Is null";
    } else if (typeof object != "object") {
        text += "Object has no properties; is of type " + typeof object + " with value: " + object;
    } else if (object instanceof Date) {
        text += object.toString();
    } else if (object instanceof RegExp) {
        text += object.source;
    } else {
        for (var i in object) {
            text += i + (": " + object[i] + "\n");
        }
    }
    alert(text);
}

var failures = {'NFC':[], 'NFD':[], 'NFKC':[], 'NFKD':[], 'preserve':[]},
    normForms = ['NFC', 'NFD', 'NFKC', 'NFKD'],
    _xf = new XQueryFunctions();

function _differentFromNF (x) {
    return function (nf) {
        return x !== _xf['normalize-unicode'](x, nf);
    };
}

function _some (arr, cb) {
    var len = arr.length, thisp = arguments[2];
    for (var i = 0; i < len; i++) {
        if (i in arr && cb.call(thisp, arr[i], i, arr)) {
            return true;
        }
    }
    return false;
}

function _write (str) {
    document.write('<textarea style="float:left;" rows="30" cols="30">'+str+'</textarea>');
}
function _fixedFromCharCode (codePt) {
    if (codePt > 0xFFFF) {
        codePt -= 0x10000;
        return String.fromCharCode(0xD800 + (codePt >> 10), 0xDC00 +
(codePt & 0x3FF));
    }
    else {
        return String.fromCharCode(codePt);
    }
}
function _map (arr, cb) {
    for (var i = 0, arrl = arr.length; i < arrl; i++) {
        arr[i] = cb(arr[i], i);
    }
    return arr;
}
function _indexOf (arr, item) {
    for (var i = 0, arrl = arr.length; i < arrl; i++) {
        if (arr[i] === item) {
            return i;
        }
    }
    return -1;
}
function loadFile(url, cb) {
    var xmlHttp, args = arguments;
    try {
        xmlHttp = new XMLHttpRequest();
    }
    catch (e) {
        try {
            xmlHttp = new ActiveXObject('Msxml2.XMLHTTP');
        }
        catch(e) {
            xmlHttp = new ActiveXObject('Microsoft.XMLHTTP');
        }
    }
    xmlHttp.onreadystatechange = function () {
        if (xmlHttp.readyState == 4) {
            if (args.length > 2) {
                cb.apply(this, [xmlHttp.responseText].concat(Array.prototype.slice.call(args, 2)));
            }
            else {
                cb(xmlHttp.responseText);
            }
        }
    };
    xmlHttp.open('get', url, true);
    xmlHttp.send(null);
}

function processNormalizationTests (txt, testTypes) {
    
    var nfcTest = _indexOf(testTypes, 'NFC') > -1,
        nfdTest = _indexOf(testTypes, 'NFD') > -1,
        nfkcTest = _indexOf(testTypes, 'NFKC') > -1,
        nfkdTest = _indexOf(testTypes, 'NFKD') > -1,
        preserveValueTest = _indexOf(testTypes, 'preserve') > -1;
    
    function _addFailure (type, failure) {
        failures[type].push(failure);
    }
    function _convertStrCpToInt (cp) {
        return parseInt('0x'+cp, 16);
    }
    function _convertStrCpToString (cp) {
        return _fixedFromCharCode(_convertStrCpToInt(cp));
    }
    function _convertToString (s) {
        return _map(s.split(/\s+/), _convertStrCpToString).join('');
    }
    function _n (args, nf) {
        var arg = _convertToString(args);
        return _xf['normalize-unicode'](arg, nf);
    }
    function _checkNonKompatibility (nf1, nf2, line, type) {
        nf1 = _convertToString(nf1);
        nf2 = _convertToString(nf2);
        var cp = line[1],
                nfc = line[2],
                nfd = line[3],
                nfkc = line[4],
                nfkd = line[5];
        if (nf1 !== _n(cp, type) || nf1 !== _n(nfc, type) || nf1 !== _n(nfd, type) ||
              nf2 !== _n(nfkc, type) || nf2 !== _n(nfkd, type)) {
            _addFailure(type, line);
        }
    }
    function _checkKompatibility (nf, line, type) {
        nf = _convertToString(nf);
        var cp = line[1],
                nfc = line[2],
                nfd = line[3],
                nfkc = line[4],
                nfkd = line[5];       
        if (nf !== _n(cp, type) || nf !== _n(nfc, type) || nf !== _n(nfd, type) ||
            nf !== _n(nfkc, type) || nf !== _n(nfkd, type)) {
            _addFailure(type, line);
        }       
    }

    var cps = [], 
        res, 
        parts = /\n@Part(\d).*\r?\n([\s\S]*?)(?:\n@Part|$)/g, 
        lines = /^(.*?);(.*?);(.*?);(.*?);(.*?);\s*#/gm
        ;
    while ((res = parts.exec(txt)) != null) {
        parts.lastIndex -= 6;
        if (res) {
            var partNo = res[1];

            while ((line = lines.exec(res[2])) != null) {
                var cp = line[1],
                    nfc = line[2],
                    nfd = line[3],
                    nfkc = line[4],
                    nfkd = line[5];
                if (partNo === '1') {
                    cps.push(cp);
                }

                if (nfcTest) {
                    _checkNonKompatibility(nfc, nfkc, line, 'NFC');
                }
                if (nfdTest) {
                    _checkNonKompatibility(nfd, nfkd, line, 'NFD');
                }
                if (nfkcTest) {
                    _checkKompatibility(nfkc, line, 'NFKC');
                }
                if (nfkdTest) {
                    _checkKompatibility(nfkd, line, 'NFKD');
                }
            }
        }
    }
    
    _map(testTypes, function (testType) {
        if (_indexOf(normForms, testType) > -1) {
            _write('Failures (' + testType + '): ' + failures[testType].length + ' ' + failures[testType] + '<br>');
        }
    });
    
    if (preserveValueTest) { // We need to check all code points not listed in part 1
        var n = 0, 
            inTable = _map(cps, _convertStrCpToInt);
        while (n < 1114111) {
            var x = _fixedFromCharCode(n);
            if (_indexOf(inTable, n) === -1) {
                if (_some(normForms, _differentFromNF(x))) {
                    _addFailure('preserve', x);
                }
            }
            n++;
        }
        _write('Failures (Part 1 preserve): ' + failures.preserve.length + ' ' + failures.preserve + '<br>');
    }
}

loadFile('NormalizationTest.txt', processNormalizationTests, [
            // 'NFC', 
            'NFD', 
            // 'NFKC', 
            'NFKD'
            // 'preserve' // VERY VERY SLOW!!!!
        ]); 


</script>
</head><body>

</body></html>
